<template>
	<view class="tts-container">
		<view class="header">
			<text class="title">曼波配音生成</text>
			<text class="subtitle">输入文本，生成 MP3 并播放</text>
		</view>

		<textarea
			v-model="text"
			class="input"
			placeholder="请输入要转换的文本..."
			:maxlength="500"
			auto-height
		/>

		<view class="actions">
			<button class="btn" :disabled="!text || loading" @click="generate">
				<view class="btn-content">
					<text v-if="loading" class="spinner">⏳</text>  <!-- spinner -->
					<text>{{ loading ? '生成中...' : '生成语音' }}</text>
				</view>
			</button>
			<button class="btn-outline" :disabled="!audioUrl" @click="clearAudio">清空</button>
		</view>

		<view v-if="errorMsg" class="error">{{ errorMsg }}</view>

		<view v-if="audioUrl" class="player-section">
			<!-- 自定义音频播放器：简化版 -->
			<view class="custom-player">
				<!-- 播放控件行：按钮左 + 进度中 + 时间右 -->
				<view class="player-controls-row">
					<button class="play-btn" @click="togglePlay" :disabled="isLoadingAudio || loading">
						<text v-if="!isPlaying && !isLoadingAudio">▶️</text>
						<text v-else-if="isPlaying">⏸️</text>
						<text v-else>⏳</text>  <!-- 加载⏳ -->
					</button>
					
					<!-- 进度条 -->
					<slider 
						v-if="duration > 0 && !isLoadingAudio" 
						class="progress-slider" 
						:value="currentTime" 
						:min="0" 
						:max="duration" 
						@change="onSliderChange"
						show-value
					/>
					
					<!-- 时间显示 -->
					<view class="time-display-compact">
						<text>{{ formatTime(currentTime) }}</text>
						<text>/</text>
						<text>{{ formatTime(duration) }}</text>
					</view>
				</view>
				
				<!-- URL容器 + 复制按钮 -->
				<view class="audio-url-container">
					<text class="audio-url" selectable>{{ audioUrl }}</text>
					<button class="copy-btn" @click="copyAudioUrl">复制链接</button>
				</view>
			</view>
		</view>

		<!-- 迷你悬浮播放器 -->
		<view v-if="audioUrl && isPlaying" class="mini-player" @touchstart="onDragStart" @touchmove="onDragMove" @touchend="onDragEnd" :style="miniStyle">
			<view class="mini-left" @click.stop="togglePlay">
				<text>{{ isPlaying ? '⏸️' : '▶️' }}</text>
			</view>
			<view class="mini-center" @click.stop="seekBy(5)">
				<text class="mini-title">已生成音频</text>
				<text class="mini-time">{{ formatTime(currentTime) }} / {{ formatTime(duration) }}</text>
			</view>
			<view class="mini-right" @click.stop="seekBy(-5)">
				<text>↩︎5s</text>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				text: '',
				audioUrl: '',
				loading: false,
				errorMsg: '',
				isPlaying: false,
				currentTime: 0,
				duration: 0,
				innerAudioContext: null,  // 新增：UniApp音频上下文
				miniX: 20,
				miniY: 20,
				dragging: false,
				startX: 0,
				startY: 0,
				baseX: 0,
				baseY: 0,
				isLoadingAudio: false,  // 用于播放加载，整合到按钮
			}
		},
		computed: {
			miniStyle() {
				const x = typeof this.miniX === 'number' ? this.miniX : 20;
				const y = typeof this.miniY === 'number' ? this.miniY : 20;
				return `transform: translate(${x}rpx, ${-y}rpx); bottom: 20rpx; left: 20rpx;`;
			}
		},
		onLoad() {
			this.initAudioContext()
		},
		onUnload() {
			if (this.innerAudioContext) {
				this.innerAudioContext.destroy()
				this.innerAudioContext = null
			}
		},
		methods: {
			async generate() {
				if (!this.text || this.loading) return;
				this.loading = true;
				this.errorMsg = '';
				this.isLoadingAudio = true;  // 同步播放加载
				try {
					const api = `https://api.milorapart.top/apis/mbAIsc?text=${encodeURIComponent(this.text)}`;
					let resData;

					// H5 使用 fetch
					// #ifdef H5
					const res = await fetch(api, { method: 'GET' });
					resData = await res.json().catch(async () => {
						const t = await res.text();
						return { data: t };
					});
					// #endif

					// 非 H5 使用 uni.request
					// #ifndef H5
					const res2 = await new Promise((resolve, reject) => {
						uni.request({ url: api, method: 'GET', success: resolve, fail: reject });
					});
					resData = res2.data;
					// #endif

					const url = this.extractUrl(resData);
					if (!url) throw new Error('未获取到音频地址');
					this.audioUrl = url;
					uni.showToast({ title: '生成成功', icon: 'success' });
				} catch (e) {
					console.error('生成语音失败:', e);
					this.errorMsg = e?.message || '生成失败';
					uni.showToast({ title: this.errorMsg, icon: 'error' });
				} finally {
					this.loading = false;
					this.isLoadingAudio = false;  // 结束加载
				}
			},
			extractUrl(data) {
				if (!data) return '';
				if (typeof data === 'string') {
					const m = data.match(/https?:\/\/[^\s"']+\.mp3/);
					return m ? m[0] : '';
				}
				const candidates = [data.url, data.voice, data.audio, data.data, data.result, data.link, data.path].filter(Boolean);
				for (const c of candidates) {
					if (typeof c === 'string') {
						const m = c.match(/https?:\/\/[^\s"']+\.mp3/);
						if (m) return m[0];
					}
				}
				try {
					const deep = data.data && (data.data.url || data.data.voice || data.data.audio);
					if (deep) {
						const m = String(deep).match(/https?:\/\/[^\s"']+\.mp3/);
						if (m) return m[0];
					}
				} catch (e) {}
				return '';
			},
			initAudioContext() {
				this.innerAudioContext = uni.createInnerAudioContext()
				this.innerAudioContext.volume = 1  // 设置初始音量
				this.innerAudioContext.onCanplay(() => {
					this.isLoadingAudio = false
					this.duration = this.innerAudioContext.duration || 0
					console.log('音频加载完成，时长:', this.duration)
				})
				this.innerAudioContext.onWaiting(() => {  // 新增：加载中状态
					this.isLoadingAudio = true
				})
				this.innerAudioContext.onPlay(() => {
					this.isPlaying = true
				})
				this.innerAudioContext.onPause(() => {
					this.isPlaying = false
				})
				this.innerAudioContext.onEnded(() => {
					this.isPlaying = false
					this.currentTime = 0
				})
				this.innerAudioContext.onTimeUpdate(() => {
					this.currentTime = this.innerAudioContext.currentTime || 0
				})
				this.innerAudioContext.onError((err) => {
					console.error('音频播放错误:', err)
					this.errorMsg = '音频播放失败: ' + (err.errMsg || '未知错误')
				})
			},
			togglePlay() {
				if (!this.innerAudioContext) return
				if (this.isLoadingAudio || this.loading) return  // 生成/加载中不播放
				if (this.isPlaying) {
					this.innerAudioContext.pause()
				} else {
					this.innerAudioContext.src = this.audioUrl
					this.isLoadingAudio = true
					this.innerAudioContext.play()
					// 监听结束加载
					const onLoaded = () => {
						this.isLoadingAudio = false
						this.innerAudioContext.offCanplay(onLoaded)  // 一次性监听
					}
					this.innerAudioContext.onCanplay(onLoaded)
				}
			},
			seekBy(delta) {
				if (!this.innerAudioContext || this.duration <= 0) return
				const newTime = Math.max(0, Math.min(this.innerAudioContext.currentTime + delta, this.duration))
				this.innerAudioContext.seek(newTime)
			},
			onSliderChange(e) {
				if (!this.innerAudioContext || this.duration <= 0) return
				const value = e.detail.value || 0
				this.innerAudioContext.seek(value)
			},
			clearAudio() {
				if (this.innerAudioContext) {
					this.innerAudioContext.stop()
				}
				this.audioUrl = ''
				this.isPlaying = false
				this.currentTime = 0
				this.duration = 0
				this.miniX = null; this.miniY = null; this.dragging = false
			},
			formatTime(sec) {
				const s = Math.floor(sec || 0);
				const m = Math.floor(s / 60);
				const r = s % 60;
				return `${String(m).padStart(2,'0')}:${String(r).padStart(2,'0')}`;
			},
			// ===== 悬浮条拖拽与吸附 =====
			onDragStart(e) {
				this.dragging = true;
				const t = e.changedTouches && e.changedTouches[0];
				this.startX = t ? t.clientX : 0;
				this.startY = t ? t.clientY : 0;
				this.baseX = this.miniX ?? 20; // 与样式保持一致
				this.baseY = this.miniY ?? 20;
			},
			onDragMove(e) {
				if (!this.dragging) return;
				const t = e.changedTouches && e.changedTouches[0];
				if (!t) return;
				this.miniX = Math.max(0, this.baseX + (t.clientX - this.startX));
				this.miniY = Math.max(0, this.baseY + (t.clientY - this.startY));
			},
			onDragEnd() {
				this.dragging = false;
				// 简易吸附：靠左或靠右
				try {
					const sys = uni.getSystemInfoSync();
					const screenW = sys.windowWidth || 375;
					if (this.miniX != null) {
						this.miniX = this.miniX < screenW / 2 ? 20 : screenW - 20 - 200; // 200 约等于宽度
					}
				} catch (e) {}
			},
			// 新增：复制URL
			copyAudioUrl() {
				if (!this.audioUrl) return
				uni.setClipboardData({
					data: this.audioUrl,
					success: () => {
						uni.showToast({ title: 'URL已复制', icon: 'success' })
					}
				})
			}
		}
	}
</script>

<style scoped>
	.tts-container {
		min-height: 100vh;
		background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
		padding: 12rpx;  /* 减小padding防溢出 */
	}
	.header {
		text-align: center;
		margin-bottom: 16rpx;  /* 减小间距 */
	}
	.title {
		font-size: 36rpx;
		font-weight: bold;
		color: #333;
		display: block;
		margin-bottom: 8rpx;
	}
	.subtitle {
		font-size: 24rpx;
		color: #666;
		display: block;
	}
	.input {
		width: calc(100% - 32rpx);  /* 预留padding空间，防溢出 */
		height: 300rpx;  /* 固定高度，防超出 */
		max-height: 300rpx;  /* 限制最大 */
		overflow-y: auto;  /* 滚动如果超长 */
		background: #fff;
		border-radius: 16rpx;
		padding: 16rpx;  /* 减小padding */
		box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
		margin: 0 auto 16rpx;  /* 居中 + 底部间距 */
		word-wrap: break-word;  /* 长文本换行 */
		resize: none;  /* 禁用resize */
		box-sizing: border-box;  /* 包含padding，防溢出 */
	}
	.actions {
		display: flex;
		gap: 16rpx;
		margin-bottom: 16rpx;
	}
	.btn {
		background: #667eea;
		color: #fff;
		border: none;
		border-radius: 8rpx;
		padding: 12rpx 20rpx;
		font-size: 24rpx;
		font-weight: bold;
		min-width: 160rpx;
		display: flex;
		align-items: center;
		justify-content: center;
	}
	.btn-content {
		display: flex;
		align-items: center;
		gap: 8rpx;  /* 文字和spinner间距 */
	}
	.spinner {
		font-size: 24rpx;
		animation: spin 1s linear infinite;  /* 旋转动画 */
	}
	@keyframes spin {
		from { transform: rotate(0deg); }
		to { transform: rotate(360deg); }
	}
	.btn:disabled {
		background: #ccc;
		color: #999;
	}
	.btn-outline {
		background: #ffffff;
		color: #667eea;
		border: 2rpx solid #667eea;
		border-radius: 8rpx;
		padding: 12rpx 20rpx;
		font-size: 24rpx;
		font-weight: bold;
		min-width: 160rpx;
	}
	.btn-outline:disabled {
		color: #ccc;
		border-color: #ccc;
	}
	.error {
		color: #e53935;
		font-size: 22rpx;
		margin-bottom: 16rpx;
		text-align: center;
	}
	.player-section {
		background: #fff;
		border-radius: 16rpx;
		padding: 16rpx;  /* 减小padding */
		box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
		margin-bottom: 16rpx;
	}
	.custom-player {
		width: 100%;
	}
	.player-controls-row {
		display: flex;
		align-items: center;
		gap: 20rpx;  /* 按钮-进度-时间间距 */
		margin-bottom: 16rpx;
		flex-wrap: wrap;  /* 小屏换行 */
	}
	.play-btn {
		background: #667eea;
		color: #fff;
		border: none;
		border-radius: 50%;
		width: 80rpx;
		height: 80rpx;
		font-size: 40rpx;
		display: flex;
		align-items: center;
		justify-content: center;
		flex-shrink: 0;  /* 固定宽度 */
		margin-left: 0;  /* 确保左侧 */
	}
	.progress-slider {
		flex: 1;  /* 占用中间空间 */
		min-width: 200rpx;  /* 最小宽度 */
		height: 6rpx;
		margin: 0 10rpx;  /* 与按钮/时间间距 */
	}
	.progress-slider::after {
		border-radius: 3rpx;
	}
	.time-display-compact {
		display: flex;
		align-items: center;
		gap: 4rpx;
		font-size: 20rpx;
		color: #333;
		min-width: 80rpx;
		flex-shrink: 0;
		text-align: right;  /* 时间右对齐 */
	}
	.audio-url-container {
		width: 100%;
		background: #f8f9fa;  /* 浅背景突出显示 */
		border-radius: 8rpx;
		padding: 12rpx;
		border: 1rpx solid #e0e0e0;
		margin-top: 8rpx;  /* 与进度条间距 */
		box-sizing: border-box;
		display: flex;
		align-items: flex-end;
		gap: 12rpx;
	}
	.audio-url {
		font-size: 20rpx;
		color: #666;
		word-break: break-all;
		line-height: 1.4;
		display: block;
		width: 100%;
	}
	.mini-player {
		position: fixed;
		left: 20rpx;
		bottom: 20rpx;
		width: 620rpx;
		height: 88rpx;
		background: rgba(255,255,255,0.96);
		border-radius: 44rpx;
		box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.12);
		display: flex;
		align-items: center;
		justify-content: space-between;
		padding: 0 20rpx;
		backdrop-filter: saturate(180%) blur(8px);
		z-index: 999;
	}
	.mini-left, .mini-right { width: 80rpx; text-align: center; font-size: 28rpx; }
	.mini-center { flex: 1; text-align: center; }
	.mini-title { display: block; font-size: 24rpx; color: #333; }
	.mini-time { display: block; font-size: 20rpx; color: #666; }

	/* 移除旧加载指示和音量样式（清理） */

	.copy-btn {
		background: #4CAF50;
		color: white;
		border: none;
		border-radius: 6rpx;
		padding: 8rpx 16rpx;
		font-size: 20rpx;
		flex-shrink: 0;
		min-width: 80rpx;
	}
	.copy-btn:active {
		background: #45a049;
	}

	/* 小屏优化：输入和播放器 */
	@media (max-width: 375px) {
		.input {
			height: 250rpx;  /* 小屏减小高度 */
			padding: 12rpx;  /* 小屏进一步减小padding */
			width: calc(100% - 24rpx);  /* 小屏预留更少空间 */
		}
		.player-controls-row {
			flex-direction: column;
			gap: 12rpx;
		}
		.progress-slider {
			order: 2;
			width: 100%;
			margin: 0;
		}
		.play-btn {
			order: 1;
		}
		.time-display-compact {
			order: 3;
		}
		.audio-url-container {
			flex-direction: column;
			align-items: stretch;
			gap: 8rpx;
		}
		.copy-btn {
			align-self: flex-end;
		}
	}
</style> 